// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/* Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * Lombo VISS-VIC controller register definitions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */

/******************************************************************************
 * base operations
 *****************************************************************************/

#include <mach/csp.h>
#include "../viss-const.h"
#include "vic_csp.h"

/* #define ENABLE_TRACE */
#include <mach/debug.h>

reg_vic_t *g_vic = (reg_vic_t *)BASE_VIC;

void csp_vic_set_register_base(void *base)
{
	g_vic = (reg_vic_t *)base;
}

/**
 * VIC enable
 */
u32 csp_vic_enable(void)
{
	reg_vic_vic_ctrl_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.en = 1;
	WRITEREG32(&(g_vic->vic_ctrl), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}


/**
 * VIC disable
 */
u32 csp_vic_disable(void)
{
	reg_vic_vic_ctrl_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.en = 0;
	WRITEREG32(&(g_vic->vic_ctrl), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * VIC EAV or SAV checkout error ints enable
 */
u32 csp_vic_ints_enable(void)
{
	reg_vic_int_en_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.chkout_error = 1;
	WRITEREG32(&(g_vic->int_en), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * VIC EAV or SAV checkout error ints disable
 */
u32 csp_vic_ints_disable(void)
{
	reg_vic_int_en_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.chkout_error = 0;
	WRITEREG32(&(g_vic->int_en), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * VIC EAV or SAV checkout error ints pending status
 */
void csp_vic_clear_pd(void)
{
	reg_vic_int_clr_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.chkout_error = 0;
	WRITEREG32(&(g_vic->int_clr), tmpreg.val);
	PRT_TRACE_END("\n");
}

/**
 * VIC EAV or SAV checkout error ints pending status
 */
u32 csp_vic_get_int_pd(void)
{
	unsigned long reg_addr = 0;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&(g_vic->int_pend);
	PRT_TRACE_END("pd=0x%08x\n", READREG32(reg_addr));

	return READREG32(reg_addr);
}

/**
 * VIC status
 *	return 0: free
 *	return 1: busy
 * Before disable DMA or VIC, the busy status should be 0
 */
u32 csp_vic_status(void)
{
	reg_vic_vic_cfg_t tmpreg;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_vic->vic_cfg));
	PRT_TRACE_END("tmpreg.bits.busy=%d\n", tmpreg.bits.busy);

	return tmpreg.bits.busy;
}

/******************************************************************************
 * interface configure
 *****************************************************************************/

/**
 * enable vic output high 8bit data
 */
u32 csp_vic_output_high_8bit_data(u32 enable)
{
	reg_vic_vic_cfg_t tmpreg;

	tmpreg.val = READREG32(&(g_vic->vic_cfg));
	tmpreg.bits.data_8bit = enable;
	WRITEREG32(&(g_vic->vic_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * enable vic output raw msb data
 */
u32 csp_vic_output_raw_msb_data(u32 enable)
{
	reg_vic_vic_cfg_t tmpreg;

	tmpreg.val = READREG32(&(g_vic->vic_cfg));
	tmpreg.bits.raw_msb = enable;
	WRITEREG32(&(g_vic->vic_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select VIC data path
 * @path:
 *	0: WB_DMA channel, by default
 *	1: Pixel channel to ISP
 *	2: Reserved
 *	3.VILD
 *	4.VILD and WB_DMA
 */
u32 csp_vic_data_path(u32 path)
{
	reg_vic_vic_cfg_t tmpreg;

	PRT_TRACE_END("path=%d\n", path);
	tmpreg.val = READREG32(&(g_vic->vic_cfg));
	tmpreg.bits.path = path;
	WRITEREG32(&(g_vic->vic_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set PCLK polarity
 * @pol: PCLK_INV
 *	0: Polarity not inverted
 *	1: Polarity inverted
 */
u32 csp_vic_set_pclk_polarity(u32 pol)
{
	reg_vic_io_cfg_t tmpreg;

	PRT_TRACE_BEGIN("pol=%d\n", pol);
	tmpreg.val = READREG32(&(g_vic->io_cfg));
	tmpreg.bits.pclk_inv = pol;
	WRITEREG32(&(g_vic->io_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set Filed polarity
 * @pol: FIELD_INV
 *	0: Polarity not inverted
 *	1: Polarity inverted
 */
u32 csp_vic_set_field_polarity(u32 pol)
{
	reg_vic_io_cfg_t tmpreg;

	PRT_TRACE_BEGIN("pol=%d\n", pol);
	tmpreg.val = READREG32(&(g_vic->io_cfg));
	tmpreg.bits.field_inv = pol;
	WRITEREG32(&(g_vic->io_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set HSYNC polarity
 * @pol: HS_INV
 *	0: Polarity not inverted
 *	1: Polarity inverted
 */
u32 csp_vic_set_hsync_polarity(u32 pol)
{
	reg_vic_io_cfg_t tmpreg;

	PRT_TRACE_BEGIN("pol=%d\n", pol);
	tmpreg.val = READREG32(&(g_vic->io_cfg));
	tmpreg.bits.hs_inv = pol;
	WRITEREG32(&(g_vic->io_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set VSYNC polarity
 * @pol: VS_INV
 *	0: Polarity not inverted
 *	1: Polarity inverted
 */
u32 csp_vic_set_vsync_polarity(u32 pol)
{
	reg_vic_io_cfg_t tmpreg;

	PRT_TRACE_BEGIN("pol=%d\n", pol);
	tmpreg.val = READREG32(&(g_vic->io_cfg));
	tmpreg.bits.vs_inv = pol;
	WRITEREG32(&(g_vic->io_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set PCLK phase
 * @phase:
 *	Delay time = phase_value * 0.2ns
 */
u32 csp_vic_set_pclk_phase(u32 phase)
{
	reg_vic_io_cfg_t tmpreg;

	PRT_TRACE_BEGIN("phase=%d\n", phase);
	tmpreg.val = READREG32(&(g_vic->io_cfg));
	tmpreg.bits.pclk_phase = phase;
	WRITEREG32(&(g_vic->io_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select DVP data bus protocol
 * @ptcl: DATA_PTCL
 *	0: RAW8
 *	1: RAW10
 *	2: RAW12
 *	3: YUV422 8-bit interleaved
 *	5: YUV422 8-bit separated, which means data bus is 16-bit
 */
u32 csp_vic_select_data_protocol(u32 ptcl)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("ptcl=%d\n", ptcl);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.data_ptcl = ptcl;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select YUV componet sequence
 * @seq: YC_SEQ, For YUV422 8-bit interleaved
 *	0: C0/C1/C2/C3
 *	1: C2/C1/C0/C3
 *	2: C1/C0/C3/C2
 *	3: C1/C2/C3/C0
 * For YUV422 16-bit separated
 *	0: D[15:8]->Y0Y1, D[7:0]->UV
 *	1: D[15:8]->Y0Y1, D[7:0]->VU
 *	2: D[15:8]->UV, D[7:0]->Y0Y1
 *	3: D[15:8]->VU, D[7:0]->Y0Y1
 * In order to ensure YUV SP or Planar mode output correct data,
 * the YUV output component sequence should be adjusted to UYVY
 */
u32 csp_vic_set_yuv_seq(u32 seq)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("seq=%d\n", seq);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.yc_seq = seq;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select embedded SYNC code position for BT.1120 mode0, 16-bit data bus
 * @pos: YC_SYNC
 *	0: Select SYNC code on D[7:0]
 *	1: Select SYNC code on D[15:8]
 */
u32 csp_vic_select_sync_code_position(u32 pos)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("pos=%d\n", pos);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.yc_sync = pos;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select SYNC protocol
 * @ptcl: SYNC_PTCL
 *	0: DC mode, external reference with fixed HREF
 *	1: JEPG mode, external reference with variable HREF
 *	2: BT.601 mode0/2/3, external HSYNC and VSYNC
 *	3: BT.656 and BT.1120 mode0, embedded SYNC with FF0000XY
 *	4: BT.1120 mode1, embedded SYNC with FFFF00000000XYXY
 *	5: BT.601 mode1, external HSYNC without VSYNC
 */
u32 csp_vic_select_sync_protocol(u32 ptcl)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("ptcl=%d\n", ptcl);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.sync_ptcl = ptcl;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select Field signal detection
 * @det: FIELD_DET
 *	0: Embedded SYNC code, such as BT.656 and BT.1120 mode0/1
 *	1: Field signal, such as BT.601 mode1/2
 *	2: Phase between VSYNC and HSYNC, such as BT.601 mode0/3
 */
u32 csp_vic_select_field_detecton(u32 det)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("det=%d\n", det);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.field_det = det;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select input video mode
 * @mode: V_MODE
 *	0: progressive, such as DC mode, 720P, 1080P
 *	1: Interlaced, such as NTSC, PAL, 1080I
 */
u32 csp_vic_select_video_mode(u32 mode)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("mode=%d\n", mode);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.v_mode = mode;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select sampling mode
 * @mode: PCLK_SM
 *	0: SDR
 *	1: DDR
 */
u32 csp_vic_select_pclk_sm(u32 mode)
{
	reg_vic_if_cfg_t tmpreg;

	PRT_TRACE_BEGIN("mode=%d\n", mode);
	tmpreg.val = READREG32(&(g_vic->if_cfg));
	tmpreg.bits.pclk_sm = mode;
	WRITEREG32(&(g_vic->if_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set HSYNC front blanking offset, Only for BT.601 mode
 * Front blanking offset = HSW + HBP
 */
u32 csp_vic_hsync_fb_offset(u32 offset)
{
	reg_vic_hfb_ofs_t tmpreg;

	PRT_TRACE_BEGIN("offset=%d\n", offset);
	tmpreg.val = 0;
	tmpreg.bits.hoffset = offset;
	WRITEREG32(&(g_vic->hfb_ofs), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set HSYNC active width, Only for BT.601 mode
 */
u32 csp_vic_hsync_active_width(u32 width)
{
	reg_vic_hact_width_t tmpreg;

	PRT_TRACE_BEGIN("width=%d\n", width);
	tmpreg.val = 0;
	tmpreg.bits.haw = width;
	WRITEREG32(&(g_vic->hact_width), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set Field1 vertical front blanking offset, Only for BT.601 mode
 */
u32 csp_vic_f1_vsync_fb_offset(u32 offset)
{
	reg_vic_f1_vfb_ofs_t tmpreg;

	PRT_TRACE_BEGIN("offset=%d\n", offset);
	tmpreg.val = 0;
	tmpreg.bits.f1_voffset = offset;
	WRITEREG32(&(g_vic->f1_vfb_ofs), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set Field1 vertical active line, Only for BT.601 mode
 */
u32 csp_vic_f1_vsync_active_line(u32 line)
{
	reg_vic_f1_vact_line_t tmpreg;

	PRT_TRACE_BEGIN("line=%d\n", line);
	tmpreg.val = 0;
	tmpreg.bits.f1_vact_line = line;
	WRITEREG32(&(g_vic->f1_vact_line), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

u32 csp_vic_set_channel_size(u32 x, u32 y)
{
	reg_vic_ch_size_t tmpreg;

	PRT_TRACE_BEGIN("x=%d,y=%d\n", x, y);
	tmpreg.val = 0;
	tmpreg.bits.width = x;
	tmpreg.bits.height = y;
	WRITEREG32(&(g_vic->ch_size), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}


/**
 * Set Field1 vertical front blanking offset, Only for BT.601 mode
 */
u32 csp_vic_f2_vsync_fb_offset(u32 offset)
{
	reg_vic_f2_vfb_ofs_t tmpreg;

	PRT_TRACE_BEGIN("offset=%d\n", offset);
	tmpreg.val = 0;
	tmpreg.bits.f2_voffset = offset;
	WRITEREG32(&(g_vic->f2_vfb_ofs), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Set Field2 vertical active line, Only for BT.601 mode
 */
u32 csp_vic_f2_vsync_active_line(u32 line)
{
	reg_vic_f2_vact_line_t tmpreg;

	PRT_TRACE_BEGIN("line=%d\n", line);
	tmpreg.val = 0;
	tmpreg.bits.f2_vact_line = line;
	WRITEREG32(&(g_vic->f2_vact_line), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

void csp_vic_cfg_fifo(void)
{

}

/**
 * Reset VIC circuit
 */
u32 csp_vic_reset(void)
{
	/* unsigned long reg_addr; */
	reg_vic_vic_rst_t tmpval;

	PRT_TRACE_BEGIN("\n");

	/* reg_addr = (unsigned long)&g_vic + 0x600; */
	tmpval.val = READREG32(&(g_vic->vic_rst));
	tmpval.bits.rst = 1;
	WRITEREG32(&(g_vic->vic_rst), tmpval.val);
	PRT_TRACE_END("\n");
	return 0;
}

/**
 * csp_vic_chk_pd - check whether all the expected int pending have been raise
 * @func_name:	the name of the function that want dump registers
 */
void csp_vic_dump_regs(const char *func_name)
{
/*
 *	u32 offset = 0;
 *
 *	PRT_DBG(" =========== (%s) ===========\n",
 *		func_name);
 *	for (offset = 0; offset < 0x3CC; offset += 8) {
 *		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
 *			offset, READREG32((unsigned long)g_vic + offset),
 *			offset + 4,
 *			READREG32((unsigned long)g_vic + offset + 4));
 *	}
 *
 *	for (offset = 0x600; offset < 0x61C; offset += 8) {
 *		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
 *			offset, READREG32((unsigned long)g_vic + offset),
 *			offset + 4,
 *			READREG32((unsigned long)g_vic + offset + 4));
 *	}
 *	PRT_DBG(" ===========================================\n");
 */
}
